// start lazy definition
///|
enum LazyData[T] {
  Waiting(() -> T)
  Done(T)
}

///|
struct LazyRef[T] {
  mut data : LazyData[T]
}

///|
fn[T] LazyRef::extract(self : LazyRef[T]) -> T {
  match self.data {
    Waiting(thunk) => {
      let value = thunk()
      self.data = Done(value) // in-place update
      value
    }
    Done(value) => value
  }
}

///|
fn square(x : LazyRef[Int]) -> Int {
  x.extract() * x.extract()
}
// end lazy definition
